不需要物件導向程式設計去,也可以寫出 ruby 程式,如果是在寫一個有點規模的程式使用物件導向程式設計可以把事情簡單化。為什麼?因為物件導向程式設計是關於程式碼的組織跟設計。
建立一個類別就是在程式裡呈現一個概念,每一個類別都有負責的事情,例如說你可以有一個類別可以知道使用者的姓名跟信箱,或者是有一個商店類別可以管理存貨。
需要 class 關鍵字來建立一個類別
class Book
end
雖然這個類別沒有做事,但你還是可以從這個類別建立物件,每個物件都是類別獨立的產品,類別就像是藍圖而物件就是做出來的產品。每一個物件都會有所不同,因為這樣可以建立相同的物件但是物件裡的內容是不一樣的,像是有很多書而每本書的內容都不一樣。
如果已經有一個類別的話,就可以從這個類別建立物件。
book = Book.new
這個 new 方法就是去呼叫 Book,這就是建立物件的方法,也可以用這個方法搭配 ruby 內建的類別,像是可以用陣列。
array = Array.new
但這些都會建立在 ruby 裡面就需要用特別的語法。
array = []
hash = {}
string = ""
屬於自己建立的類別就需要用 new 來建立物件。
接下來讓類別變聰明開始學著做些事情,可以寫一個方法。方法是一個指令你可以重複使用這個方法也會跟特定類別有關聯。就像是變數。
class Book
def what_am_i
puts "I'm a book!"
end
end
這裡建立一個what_am_i
名稱的方法,當呼叫這個方法的時候會印出一些內容。
book = Book.new
book.what_am_i
在一個 book 的物件上呼叫一個方法而不是書本身,把這個方法稱作實體方法,因為“實體”是“物件”的另一個名稱,這些方法只會為了物件操作&存在。
物件可以保存屬於自己的資料
class Book
def initialize(title, author)
@title = title
@author = author
end
end
在這裡有一個 initialize 方法。Initialize 是一個特別的 ruby 方法當建立 ruby 物件的時候可以呼叫,用這個方法可以設定初始值。後面接著是 @ 開頭的實體變數。
實體變數就像是變數是被使用在類別裡面,那為什麼又需要實體變數?因為可以儲存資料並且分享給所有方法使用。如果沒有實體變數,用來建立 ruby 物件的資料就不能存在於 initialize 方法之外。
最後一塊拼圖是如何存取在 initialize 方法裡面的資料?有兩個方法可以做到,如果是在實體方法裡面就可以直接參照( @變數名稱 ),如果在類別外面就會需要一個屬性存取器( attribute accessor )。
舉個例子,建立兩本書
deep_dive = Book.new("Ruby Deep Dive", "Jesus Castello")
fun = Book.new("Fun With Programming", "White Cat")
如果想要存取書名
deep_dive.title
這樣會得到一個錯誤訊息
NoMethodError: undefined method 'title'
實體變數預設都是私有的,所以無法直接拿到書名。那如果想要存取就必須讓他顯現出來,就可以使用屬性存取器。
class Book
attr_reader :title, :author
def initialize(title, author)
@title = title
@author = author
end
end
屬性讀取 attr_reader 像是一個打開的窗戶,在路上的人都可以看到窗戶裡面的東西也可以拿得到。如果再重新讀取書的作者的資料這次就會成功。
deep_dive.author
# "Jesus Castello"
這個不是什麼很特別的事, attr_reader 只是一個撰寫程式碼的捷徑,其實他的原型是長這樣:
def author
@author
end
因為方法可存取實體變數所以就可以回傳值,下面介紹其他的屬性存取器:
attr_reader (read-only)
attr_writer (write-only)
attr_accessor (read & write)
當你使用 attr_writer 或 attr_accessor 就可以在類別外面改變實體變數的值,像是這樣:
deep_dive.title = "Ruby"
就像是一般的規則,你想要對實體變數的存取做限制就可以只要使用 attr_reader 就不要使用到 attr_accessor。
目前看到的都是關於從類別建立的實體、物件,但是也是有可能會去建立類別層級的方法,像是:
class Food
def self.cook
end
end
這個在方法名稱前面的 self 讓這個方法變成類別方法。這個 self 在這裡的關係是會參照到類別名稱(Food)。與實體方法不同的地方在於實體方法只能給物件使用,類別方法就是只給類別使用。
可以這樣呼叫
Food.cook
為什麼想要建立類別方法?也沒有一定要。因為大部分的時間都是在操作實體方法。但是有些時候就是不太需要建立物件,例如使用 Math 類別做計算的時候:
Math.sqrt(25)
這時候就不需要特別再去建立一個 Math 物件,因為 Math 類別不需要儲存資料。這個在 Math 的方法會需要一個參數,計算完之後會吐一個答案,這是類別方法的使用案例。